import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext';
import { LexicalTypeaheadMenuPlugin } from '@lexical/react/LexicalTypeaheadMenuPlugin';
import { $createTextNode, $getSelection, $isRangeSelection, TextNode } from 'lexical';
import { useCallback, useState, useEffect, useRef } from 'react';
import * as ReactDOM from 'react-dom';

import { SvgIcon } from '@/components/icon';
import { t } from '@/locales/i18n';
import { WorkflowIOValueTypeEnum } from '@/types/workflow/constant';

import { EditorVariableLabelPickerType } from '../../type';
import { useBasicTypeaheadTriggerMatch } from '../../utils';

interface EditorVariableItemType {
  key: string;
  label: string;
  required?: boolean;
  icon?: string;
  valueType?: WorkflowIOValueTypeEnum;
  index: number;
}
interface TransformedParent {
  id: string;
  label: string;
  avatar: string;
  children: EditorVariableItemType[];
}

export default function VariableLabelPickerPlugin({
  variables,
  isFocus,
}: {
  variables: EditorVariableLabelPickerType[];
  isFocus: boolean;
}) {
  const [editor] = useLexicalComposerContext();
  const [queryString, setQueryString] = useState<string | null>(null);
  const [currentIndex, setCurrentIndex] = useState<number>(0);
  const highlightedItemRef = useRef<any>(null);

  const checkForTriggerMatch = useBasicTypeaheadTriggerMatch('/', {
    minLength: 0,
  });

  const onSelectOption = useCallback(
    (selectedOption: any, nodeToRemove: TextNode | null, closeMenu: () => void) => {
      editor.update(() => {
        const selection = $getSelection();
        if (!$isRangeSelection(selection) || selectedOption == null) {
          return;
        }
        if (nodeToRemove) {
          nodeToRemove.remove();
        }
        selection.insertNodes([
          $createTextNode(`{{$${selectedOption.parent?.id}.${selectedOption.key}$}}`),
        ]);
        closeMenu();
      });
    },
    [editor],
  );

  useEffect(() => {
    if (highlightedItemRef.current) {
      highlightedItemRef.current.scrollIntoView({
        behavior: 'auto',
        block: 'end',
      });
    }
  }, [currentIndex]);

  return (
    <LexicalTypeaheadMenuPlugin
      onQueryChange={setQueryString}
      onSelectOption={onSelectOption}
      triggerFn={checkForTriggerMatch}
      options={variableFilter(variables, queryString || '')}
      menuRenderFn={(anchorElementRef, { selectedIndex, selectOptionAndCleanUp }) => {
        if (anchorElementRef.current == null) {
          return null;
        }
        if (currentIndex !== selectedIndex) {
          setCurrentIndex(selectedIndex || 0);
        }
        return anchorElementRef.current && variables.length && isFocus
          ? ReactDOM.createPortal(
              <div className="z-50 max-h-60 w-auto min-w-[240px] overflow-auto rounded-md border border-gray-200 bg-white p-1.5 shadow-lg">
                {variableFilter(variables, queryString || '').length === variables.length}
                {variableFilter(variables, queryString || '').length > 0 ? (
                  transformVariables(variableFilter(variables, queryString || '')).map((item) => {
                    return (
                      <div className="flex flex-col pt-2" key={item.id}>
                        <div className="mb-1.5 flex items-center">
                          <SvgIcon icon={item.avatar.replace('.svg', '') as any} />
                          <div className="mx-2 text-nowrap text-sm font-semibold text-gray-600">
                            {t(item.label as any)}
                          </div>
                        </div>
                        {item.children?.map((child) => (
                          <div
                            className={`flex cursor-pointer items-center overflow-auto rounded-md px-2 py-1 hover:bg-[#1118240D] hover:text-primary-300 ${
                              selectedIndex === child.index
                                ? 'bg-[#1118240D] text-primary'
                                : 'bg-white text-gray-600'
                            }`}
                            key={child.key}
                            // _notLast={{
                            //   mb: 1
                            // }}
                            ref={selectedIndex === child.index ? highlightedItemRef : null}
                            onMouseDown={() => {
                              selectOptionAndCleanUp({ ...child, parent: item });
                            }}
                          >
                            <div className="font-sm ml-2 text-nowrap">{child.label}</div>
                          </div>
                        ))}
                      </div>
                    );
                  })
                ) : (
                  <div className="p-2 text-sm text-gray-400">{t('unusable_variable')}</div>
                )}
              </div>,
              anchorElementRef.current,
            )
          : null;
      }}
    />
  );
}

function transformVariables(variables: EditorVariableLabelPickerType[]): TransformedParent[] {
  const transformedData: TransformedParent[] = [];
  const parentMap: { [key: string]: TransformedParent } = {};

  variables.forEach((item, index) => {
    const parentId = item.parent.id;
    const parentLabel = item.parent.label;
    const parentAvatar = item.parent.avatar;

    if (!parentMap[parentId]) {
      parentMap[parentId] = {
        id: parentId,
        label: parentLabel,
        avatar: parentAvatar || '',
        children: [],
      };
    }
    parentMap[parentId].children.push({
      label: item.label,
      key: item.key,
      icon: item.icon,
      index,
    });
  });

  const addedParents = new Set<string>();
  variables.forEach((item) => {
    const parentId = item.parent.id;
    if (!addedParents.has(parentId)) {
      transformedData.push(parentMap[parentId]);
      addedParents.add(parentId);
    }
  });

  return transformedData;
}

function variableFilter(
  variables: EditorVariableLabelPickerType[],
  queryString: string,
): EditorVariableLabelPickerType[] {
  const lowerCaseQuery = queryString.toLowerCase();

  return variables.filter((item) => {
    const labelMatch = item.label.toLowerCase().includes(lowerCaseQuery);
    const keyMatch = item.key.toLowerCase().includes(lowerCaseQuery);
    const parentLabelMatch = item.parent.label.toLowerCase().includes(lowerCaseQuery);

    return labelMatch || keyMatch || parentLabelMatch;
  });
}
